import * as THREE from 'three'
async function loadMatrial(volCanvas) {
  let container = document.getElementById( 'container' );
  let colorCube = new THREE.ShaderMaterial( {
    vertexShader: 'varying vec3 worldSpaceCoords;void main(){worldSpaceCoords = position + vec3(0.5, 0.5, 0.5);gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );}',
    fragmentShader: 'varying vec3 worldSpaceCoords;void main(){gl_FragColor = vec4( worldSpaceCoords.x , worldSpaceCoords.y, worldSpaceCoords.z, 1 );}',
    side: THREE.BackSide
  } );
  let rtTexture = new THREE.WebGLRenderTarget( container.clientWidth, container.clientHeight,
    {
      minFilter: THREE.NearestFilter,
      magFilter: THREE.NearestFilter,
      wrapS:  THREE.ClampToEdgeWrapping,
      wrapT:  THREE.ClampToEdgeWrapping,
      format: THREE.RGBFormat,
      type: THREE.FloatType,
      generateMipmaps: false
    } );//  rtTexture是用来保存渲染结果的，此时不渲染到画布canvas上
  let cubeTextures = new THREE.CanvasTexture(volCanvas);
  cubeTextures.generateMipmaps = false;
  cubeTextures.minFilter = THREE.LinearFilter;
  cubeTextures.magFilter = THREE.LinearFilter;
  let boxLen = volCanvas.width
  let material = new THREE.ShaderMaterial( {
    vertexShader: vs,
    fragmentShader: fs,
    side: THREE.FrontSide,
    uniforms: {
      tex:  { type: "t", value: rtTexture },
      cubeTex:  { type: "t", value: cubeTextures },
      boxLen: { type: "1f", value: boxLen },
      steps : {type: "1f" , value: 100.0 },
      alphaCorrection : {type: "1f" , value: 0.95 }
    }
  });
  return {colorCube,material};
}
function build(volCanvas) {
  return loadMatrial(volCanvas).then(({colorCube,material})=>{
    let boxGeometry = new THREE.BoxGeometry(1.0, 1.0, 1.0);
    boxGeometry.doubleSided = true;
    let colorCubeMesh = new THREE.Mesh( boxGeometry, colorCube );
    let volMesh = new THREE.Mesh( boxGeometry, material );
    return Promise.resolve({colorCubeMesh, volMesh})
  })
}
const fs =
  'varying vec3 worldSpaceCoords;\n' +
  'varying vec4 projectedCoords;\n' +
  'uniform sampler2D tex, cubeTex;\n' +
  'uniform float boxLen;\n' +
  'uniform float steps;\n' +
  'uniform float alphaCorrection;\n' +
  'const int MAX_STEPS = 887;\n' +
  '//Acts like a texture3D using Z slices and trilinear filtering.\n' +
  'vec4 sampleAs3DTexture( vec3 texCoord )\n' +
  '{\n' +
  '  vec4 colorSlice;\n' +
  '  vec2 texCoordSlice;\n' +
  '  float t = 1.0 - texCoord.z;\n' +
  '  texCoord.z = 1.0 - texCoord.y;\n' +
  '  texCoord.y = t;\n' +
  '  float zSliceNumber = floor(texCoord.z  * boxLen);\n' +
  '  texCoord.y /= boxLen;\n' +
  '  texCoordSlice = texCoord.xy;\n' +
  '  texCoordSlice.y += zSliceNumber / boxLen;\n' +
  '  colorSlice = texture2D( cubeTex, texCoordSlice );\n' +
  '  return colorSlice;\n' +
  '}\n' +
  'void main( void ) {\n' +
  '  //Transform the coordinates it from [-1;1] to [0;1]\n' +
  '  vec2 texc = vec2(((projectedCoords.x / projectedCoords.w) + 1.0 ) / 2.0,\n' +
  '          ((projectedCoords.y / projectedCoords.w) + 1.0 ) / 2.0 );\n' +
  '  //The back position is the world space position stored in the texture.\n' +
  '  vec3 backPos = texture2D(tex, texc).xyz;\n' +
  '  //The front position is the world space position of the second render pass.\n' +
  '  vec3 frontPos = worldSpaceCoords;\n' +
  '  //Using NearestFilter for rtTexture mostly eliminates bad backPos values at the edges\n' +
  '  //of the cube, but there may still be no valid backPos value for the current fragment.\n' +
  '  if ((backPos.x == 0.0) && (backPos.y == 0.0))\n' +
  '  {\n' +
  '    gl_FragColor = vec4(0.0);\n' +
  '    return;\n' +
  '  }\n' +
  '  //The direction from the front position to back position.\n' +
  '  vec3 dir = backPos - frontPos;\n' +
  '  float rayLength = length(dir);\n' +
  '  //Calculate how long to increment in each step.\n' +
  '  float delta = 1.0 / steps;\n' +
  '  //The increment in each direction for each step.\n' +
  '  vec3 deltaDirection = normalize(dir) * delta;\n' +
  '  float deltaDirectionLength = length(deltaDirection);\n' +
  '  //Start the ray casting from the front position.\n' +
  '  vec3 currentPosition = frontPos;\n' +
  '  //The color accumulator.\n' +
  '  vec4 accumulatedColor = vec4(0.0);\n' +
  '  //The alpha value accumulated so far.\n' +
  '  float accumulatedAlpha = 0.0;\n' +
  '  //How long has the ray travelled so far.\n' +
  '  float accumulatedLength = 0.0;\n' +
  '  //If we have twice as many samples, we only need ~1/2 the alpha per sample.\n' +
  '  //Scaling by 256/10 just happens to give a good value for the alphaCorrection slider.\n' +
  '  float alphaScaleFactor = 25.6 * delta;\n' +
  '  vec4 colorSample;\n' +
  '  float alphaSample;\n' +
  '  //Perform the ray marching iterations\n' +
  '  for(int i = 0; i < MAX_STEPS; i++)\n' +
  '  {\n' +
  '    //Get the voxel intensity value from the 3D texture.\n' +
  '    colorSample = sampleAs3DTexture( currentPosition );\n' +
  '    //Allow the alpha correction customization.\n' +
  '    alphaSample = colorSample.a * alphaCorrection;\n' +
  '    //Applying this effect to both the color and alpha accumulation results in more realistic transparency.\n' +
  '    alphaSample *= (1.0 - accumulatedAlpha);\n' +
  '    //Scaling alpha by the number of steps makes the final color invariant to the step size.\n' +
  '    alphaSample *= alphaScaleFactor;\n' +
  '    //Perform the composition.\n' +
  '    accumulatedColor += colorSample * alphaSample;\n' +
  '    //Store the alpha accumulated so far.\n' +
  '    accumulatedAlpha += alphaSample;\n' +
  '    //Advance the ray.\n' +
  '    currentPosition += deltaDirection;\n' +
  '    accumulatedLength += deltaDirectionLength;\n' +
  '    //If the length traversed is more than the ray length, or if the alpha accumulated reaches 1.0 then exit.\n' +
  '    if(accumulatedLength >= rayLength || accumulatedAlpha >= 1.0 )\n' +
  '      break;\n' +
  '  }\n' +
  '  gl_FragColor  = accumulatedColor;\n' +
  '}';
const vs =
  'varying vec3 worldSpaceCoords;\n' +
  'varying vec4 projectedCoords;\n' +
  '\n' +
  'void main()\n' +
  '{\n' +
  '  worldSpaceCoords = (modelMatrix * vec4(position + vec3(0.5, 0.5,0.5), 1.0 )).xyz;\n' +
  '  gl_Position = projectionMatrix *  modelViewMatrix * vec4( position, 1.0 );\n' +
  '  projectedCoords =  projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n' +
  '}';
export {
  build
}
